package middleware

import (
	"fmt"
	"net/http"
	"reflect"
	"strings"
	"time"

	log "github.com/sirupsen/logrus"

	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
)

func JWT() gin.HandlerFunc {
	return func(c *gin.Context) {
		p := "login,getCaptcha,getCaptcha,addUserMenu"
		pList := strings.Split(p, ",")
		for _, v := range pList {
			if strings.Contains(c.Request.URL.Path, v) {
				c.Next()
				return
			}
		}
		var data = ""
		Authorization := c.GetHeader("Authorization")

		// 处理Bearer前缀
		if strings.HasPrefix(Authorization, "Bearer ") {
			Authorization = Authorization[7:]
		}

		claims, err := ParseToken(Authorization)
		if err != nil {
			switch err.(*jwt.ValidationError).Errors {
			case jwt.ValidationErrorExpired:
				data = "Token已超时"
			default:
				data = "Token鉴权失败"
			}
		}
		if data != "" {
			c.String(http.StatusUnauthorized, data)
			c.Abort()
			return
		}
		// 将 username 存储到 Context 中
		c.Set("username", claims.Username)
		c.Set("realName", claims.RealName)

		log.Warningf("======username: %s, realname: %s=====", claims.Username, claims.RealName)

		c.Next()
	}
}

var JwtSecret = []byte("S0N8xRWTWo6DUeMAOPlC2ml+4KU46xYrfQb28m/bqNOGpvi563HYCzDIvQTrnxYi")

type Claims struct {
	UserId   int64  `json:"userId"`
	Username string `json:"username"`
	RealName string `json:"realname"`
	DepName  string `json:"depName"`
	jwt.StandardClaims
}

func GenerateToken(userId int64, username, realName, depName string) (string, int64, error) {
	nowTime := time.Now()
	expireTime := nowTime.Add(168 * time.Hour)
	claims := Claims{
		userId,
		username,
		realName,
		depName,
		jwt.StandardClaims{
			ExpiresAt: expireTime.Unix(),
			Issuer:    "https://go-admin/",
		},
	}

	tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	token, err := tokenClaims.SignedString(JwtSecret)
	return token, expireTime.Unix(), err
}

func ParseToken(token string) (*Claims, error) {
	tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		return JwtSecret, nil
	})

	if tokenClaims != nil {
		if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid {
			return claims, nil
		}
	}

	return nil, err
}

func GetIdFromClaims(key string, claims jwt.Claims) string {
	v := reflect.ValueOf(claims)
	if v.Kind() == reflect.Map {
		for _, k := range v.MapKeys() {
			value := v.MapIndex(k)

			if fmt.Sprintf("%s", k.Interface()) == key {
				return fmt.Sprintf("%v", value.Interface())
			}
		}
	}
	return ""
}

// 更新Token
func RefreshToken(token string) (string, int64, error) {
	newToken := ""
	var expired int64
	c, err := ParseToken(token)
	if err != nil {
		return "", expired, err
	} else {
		newToken, expired, err = GenerateToken(c.UserId, c.Username, c.RealName, c.DepName)
		if err != nil {
			return "", expired, err
		}
	}

	return newToken, expired, nil
}
